package com.wmyg.antifake.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.rabbitmq.client.AMQP;
import com.wmyg.antifake.bean.*;
import com.wmyg.antifake.config.Constant;
import com.wmyg.antifake.dao.*;
import com.wmyg.antifake.message.Message;
import com.wmyg.antifake.message.TrackApplyMessage;
import com.wmyg.antifake.message.TrackMessage;
import com.wmyg.antifake.sdk.FabricException;
import com.wmyg.antifake.service.AsyncTaskService;
import com.wmyg.antifake.service.FabricService;
import com.wmyg.antifake.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.hyperledger.fabric.sdk.exception.ProposalException;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Slf4j
@Service
public class FabricServiceImpl implements FabricService {

    @Autowired
    FabricDao fabricDao;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    ProductMapper productMapper;

    @Autowired
    FactoryMapper factoryMapper;

    @Autowired
    BrandMapper brandMapper;

    @Autowired
    QrcodeMapper qrcodeMapper;

    @Autowired
    private PictureMapper pictureMapper;

    @Autowired
    QrProductMapper qrProductMapper;

    @Value("${files.uploadFolder}")
    private String uploadBasePath;

    @Autowired
    AsyncTaskService asyncTaskService;

    @Autowired
    MessagePropertiesConverter messagePropertiesConverter;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public Message enterTotalList(List<UnitInfo> items, String producer) {
        Long serial = System.currentTimeMillis();
        HashMap<String, List<UnitInfo>> map = new HashMap<>();

        items.forEach(item -> {
            String prefix = item.getqR().substring(0, 39);
            Long suffix = serial;
            String batchKey = prefix + suffix;

            while (map.get(batchKey) != null && map.get(batchKey).size() >= 50000) {
                suffix ++;
                batchKey = prefix + suffix;
            }

            if (map.get(batchKey) == null) {
                ArrayList<UnitInfo> classifiedList = new ArrayList<>();
                classifiedList.add(item);
                map.put(batchKey, classifiedList);
            } else {
                map.get(batchKey).add(item);
            }
        });
        Object context = null;
        AtomicReference<String> state = new AtomicReference<>("0");
        AtomicReference<String> describe = new AtomicReference<>("操作成功");
        map.forEach((k, v) -> {

            v.get(0).setProducer(producer);
            v.get(0).setEnterTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//            try {
//                fabricDao.enterItems(v, k);
//            } catch (Throwable throwable) {
//                try {
////                    fabricDao.enterItems(v, k);
//                } catch (FileNotFoundException e) {
//                    state.set("1");
//                   // message.setDescribe("服务端配置错误:服务端找不到您的信息文件");
//                    describe.set("服务端配置错误:服务端找不到您的信息文件");
//                    e.printStackTrace();
//                } catch (ProposalException e) {
//                    state.set("1");
//                    describe.set("操作失败：非法客户端访问");
//                    e.printStackTrace();
//                } catch (TimeoutException e) {
//                    state.set("1");
//                    describe.set("操作失败：服务器操作超时");
//                    e.printStackTrace();
//                } catch (Throwable e) {
//                    state.set("1");
//                    describe.set("其他错误:" + e.getMessage());
//                    e.printStackTrace();
//                }
//            }
        });
        return new Message(state.get() , describe.get() , context);
    }

    @Override
    public Message track(String qR, QueryInfo queryInfo, HttpServletRequest request) {
        Object context = null;
        String state = "0";
        String describe = "操作成功";
        QRInfo qrInfo = null;
        try {
            TrackInfo trackInfo = new TrackInfo();
            if (redisTemplate.hasKey(qR)) {
                //TODO redis有该二维码的查询信息，拿到查询信息
                qrInfo = (QRInfo) redisTemplate.opsForValue().get(qR);
                TrackTimesResult result = this.iSearchTimes(qR,qrInfo,queryInfo);
                trackInfo.setTimes(result.getTimes());
                if (!(result.isFlag())) {
                    asyncTaskService.syncDataFabricAndRedis(qR, queryInfo);
                }
            } else {
//                qrInfo = fabricDao.query(qR, queryInfo);
                qrInfo = sendTrackMessage(qR, queryInfo);
                if (qrInfo.getQueryHistory() == null) {
                    qrInfo.setQueryHistory(new ArrayList<>());
                    qrInfo.getQueryHistory().add(queryInfo);
                }
                log.info("querytimes get for fabric is {}", qrInfo.getQueryHistory().size());
                redisTemplate.opsForValue().set(qR, qrInfo);
            }
            trackInfo.setQrInfo(qrInfo);
//            Product product = productMapper.selectById(qrInfo.getItemInfo().getItemNo());
//            trackInfo.setProduct(product);
//            Factory factory = factoryMapper.view(product.getFactoryId());
//            trackInfo.getProduct().setFactory(factory);
//            Brand brand = brandMapper.view(product.getBrandId());
//            trackInfo.getProduct().setBrand(brand);
            trackInfo.getQrInfo().getItemInfo().setQR(qR);
            context = TrackInfoConverter.convert(qR,trackInfo, request,qrcodeMapper,qrProductMapper,pictureMapper);
        } catch (FileNotFoundException e) {
            state = "1";
            describe = "服务端配置错误:服务端找不到您的信息文件";
            e.printStackTrace();
        } catch (ProposalException e) {
            state = "1";
            describe = "操作失败：" + e.getMessage();
            e.printStackTrace();
        } catch (TimeoutException e) {
            state = "1";
            describe = "操作失败：服务器操作超时" + e.getMessage();
            e.printStackTrace();
        } catch (FabricException e) {
            state = "1";
            describe = "查询错误:" + e.getMessage();
            e.printStackTrace();
        } catch (ExecutionException e) {
            state = "1";
            describe = "查询失败：此二维码正在被查询";
            e.printStackTrace();
        } catch (Throwable e) {
            state = "1";
            describe = "其他错误:" + e.getMessage();
            StringWriter stringWriter= new StringWriter();
            PrintWriter writer= new PrintWriter(stringWriter);
            e.printStackTrace(writer);
            StringBuffer buffer= stringWriter.getBuffer();
            e.printStackTrace();
            log.error(e.toString());
            log.error(buffer.toString());
        }
        return new Message(state,describe,context);
    }

 /*   @Override
    @Async(value = "asyncMoveDataToRedis")
    public void syncDataFabricAndRedis(String qR, QueryInfo queryInfo) {
        try {
            QRInfo q = fabricDao.query(qR, queryInfo);
            //q.getQueryHistory().add(queryInfo);
            //redisTemplate.opsForValue().set(qR, q);
            log.info("querytimes async get for fabric is {}>>>>>>>>>>>>>>>>>>>>>>>>>", q.getQueryHistory().size());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }*/

    @Override
    @Async(value="asyncBillInsert")
    public Future<Map<String,Object>>  genCodes( UnitInfo uParam,  String[] packArr,String[] sNumArr,String userId,Long qrcodeId) {
         //String SUFFIX_2003 = ".xls";
        // String EXCEL_PATH = "excel/";
        uParam.setNumOfSonPacks(0);
        uParam.setProducer(userId);
        Future<Map<String,Object>> future;
        Map<String,Object> baseResult = new HashMap<>();
//        List<UnitInfo> unitInfoList = new ArrayList<>();
               //数组转化
        int[] numArr = new int[sNumArr.length];
        for(int i = 0 ; i < numArr.length ; i ++){
            numArr[i] = Integer.parseInt(sNumArr[i]);
        }

        Map<String,Integer> paramMap = new HashMap<>();

        for (int i = 0 ; i < packArr.length ; i ++) {
            int unitNum = 1;
            for(int k = 0 ; k < i+1; k++){
                unitNum = unitNum * numArr[k];
            }
            paramMap.put(packArr[i], unitNum);
        }

        String dateString = new SimpleDateFormat("yyMMdd").format(new Date());
        ArrayList<Dozen> dozens = new ArrayList<>();
        paramMap.forEach((k, v) -> {
            Integer rest = v;
            Integer dozenId = 0;
            while (rest > 0) {
                String dozenIdString = MaterialCodeUtil.createDozenId(dozenId);
                if (rest >= Dozen.MAX_COUNT) {
                    dozens.add(new Dozen(k, dozenIdString, Dozen.MAX_COUNT));
                } else {
                    dozens.add(new Dozen(k, dozenIdString, rest));
                }
                rest -= Dozen.MAX_COUNT;
                dozenId++;
            }
        });
        Message message = new Message("0", "操作成功", null);
        List<CompletableFuture<Dozen>> dozenFutures = dozens.stream().map(d ->
                CompletableFuture.supplyAsync(() -> {
                    d.setQrs(new HashSet<>());
                    while (d.getQrs().size() < d.getCount()) {
                        d.getQrs().add(MaterialCodeUtil.createGS1Code2());
                    }
                    uParam.setEnterTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                    try {
                        fabricDao.enterItems(uParam, MaterialCodeUtil.montageGS1(uParam.getItemNo(), dateString,
                                uParam.getEnterBatch(), d.getLevel(), d.getDozenId(), ""), d);
                    } catch (Throwable e) {
                        throw new RuntimeException(e);
                    }
                    return d;
                }).exceptionally(e -> {
                    try {
                        fabricDao.enterItems(uParam, MaterialCodeUtil.montageGS1(uParam.getItemNo(), dateString,
                                uParam.getEnterBatch(), d.getLevel(), d.getDozenId(), ""), d);
                    } catch (Throwable t) {
                        message.setState("1");
                        message.setDescribe(e.getMessage());
                    }
                    return d;
                })).collect(Collectors.toList());
        Map <String,List<Dozen>> dozenMap = dozenFutures.stream().map(CompletableFuture::join).collect(Collectors.groupingBy(Dozen::getLevel));

//        Map <String,List<Dozen>> qrMap=new HashMap<>();
//        List<UnitInfo> caseList=new ArrayList<>();
//        List<UnitInfo> boxList=new ArrayList<>();
//        List<UnitInfo> packageList=new ArrayList<>();
//
//        if(packArr.length > 1){
//            for(int i = 0 ; i < packArr.length; i ++){
//                int unitNum = 1 ;
//                for(int k = 0 ; k < i+1; k++){
//                    unitNum = unitNum * numArr[k];
//                }
//
//                for(int j = 0 ; j < unitNum ; j ++){
//                    UnitInfo unitInfo = new UnitInfo(uParam.getProductTime(),uParam.getItemNo(),uParam.getEnterBatch(),userId,
//                            uParam.getEnvironment(),uParam.getMaterialBatch());
//                    unitInfo.setPackLevel(packArr[i]);
//                    //生成二维码
//                    unitInfo.setQR(MaterialCodeUtil.createGS1Code(unitInfo.getItemNo(),unitInfo.getEnterBatch(),unitInfo.getPackLevel()));
//                    if(i == packArr.length - 1){
//                        unitInfo.setNumOfSonPacks(0);
//                    }else{
//                        unitInfo.setNumOfSonPacks(numArr[i+1]);
//                    }
//                    unitInfoList.add(unitInfo);
//
//                    switch (packArr[i]){
//                        case "箱" :
//                            caseList.add(unitInfo);
//                            break;
//                        case "盒":
//                            boxList.add(unitInfo);
//                            break;
//                        case "包":
//                            packageList.add(unitInfo);
//                            break;
//                    }
//                }
//            }
//        }else{//
//            for(int i = 0 ; i < numArr[0] ; i ++){
//                UnitInfo unitInfo = new UnitInfo(uParam.getProductTime(),uParam.getItemNo(),uParam.getEnterBatch(),userId,
//                        uParam.getEnvironment(),uParam.getMaterialBatch());
//                unitInfo.setPackLevel(packArr[0]);
//                unitInfo.setNumOfSonPacks(0);
//                //生成二维码
//                unitInfo.setQR(MaterialCodeUtil.createGS1Code(unitInfo.getItemNo(),unitInfo.getEnterBatch(),unitInfo.getPackLevel()));
//                unitInfoList.add(unitInfo);
//            }
//            switch (packArr[0]){
//                case "箱" :
//                    caseList.addAll(unitInfoList);
//                    break;
//                case "盒":
//                    boxList.addAll(unitInfoList);
//                    break;
//                case "包":
//                    packageList.addAll(unitInfoList);
//                    break;
//            }
//        }
//        qrMap.put("箱",caseList);
//        qrMap.put("盒",boxList);
//        qrMap.put("包",packageList);
//
//        Message message = enterTotalList(unitInfoList, userId);
        if("1".equals(message.getState())){//失败
//        if (false) {
            baseResult.put("success","no");
            baseResult.put("message","二维码生成失败！");
            System.out.println(message.getDescribe());
            future=new AsyncResult<Map<String,Object>>(baseResult);
            return future;
        }

        String excelName = userId+"-"+UUID.randomUUID().toString()+ Constant.SUFFIX_2003;
        String savePath = uploadBasePath + Constant.EXCEL_PATH + excelName;
//        log.info("which in excel is {}", unitInfoList);
        boolean isGenSuss = ExcelUtil.writeExcel(uParam,dozenMap,savePath,dateString);
        baseResult.put("success","yes");
        baseResult.put("message","二维码生成成功！");
        baseResult.put("filename", excelName);

        Integer size = 0;
        for (List<Dozen> ds : dozenMap.values()) {
            for (Dozen d : ds) {
                size = size + d.getQrs().size();
            }
        }
        Qrcode qrcode = new Qrcode();
        qrcode.setQrcodeId(qrcodeId);
        qrcode.setTotalNum(size);
        qrcode.setExcelUrl(excelName);
        qrcodeMapper.updateQrcode(qrcode);

//        unitInfoList.clear();
        future=new AsyncResult<>(baseResult);
        return future;
    }


    @Override
    public PageInfo<Qrcode> findQrCode(Qrcode qrcode) {
        PageHelper.startPage(qrcode.getPageIndex(), qrcode.getPageSize(), "qrcode_id desc");
        //List<Qrcode> qrcodeList = qrcodeMapper.findQrCode(qrcode);
        List<Qrcode> qrcodeList = qrcodeMapper.findQrCodeAndFactory(qrcode);
        PageInfo result = new PageInfo(qrcodeList);
        return result;
    }

    @Override
    public Qrcode findPrimaryId(Long qrCodeId) {
        Qrcode qrcode = qrcodeMapper.findPrimaryId(qrCodeId);
        return qrcode;
    }

    @Override
    public int insertOrUpdate(Qrcode qrcode) {
        int result=qrcodeMapper.insertOrUpdate(qrcode);
        return result;
    }

    @Override
    public int deleteQrCode(Long qrCodeId) {
        int result= qrcodeMapper.deleteQrCode(qrCodeId);
        return result;
    }

    private TrackTimesResult iSearchTimes(String qR , QRInfo qrInfo , QueryInfo queryInfo){
        boolean timesFlag = false ;
        int times = qrInfo.getQueryHistory().size() ;
        String ua = queryInfo.getUserAgent();
        String ua_old = "" ;
        String[] ua_arg = ua.substring(ua.indexOf("(")+1,ua.indexOf(")")).split(";");
        for (int i= 0;i<ua_arg.length;i++) {
            if(ua_arg[i].contains("Mac") || ua_arg[i].contains("Build") || ua_arg[i].contains("Nokia")){
                ua_old = ua_arg[i] ;
                break;
            }
        }
        for(int i= 0;i<qrInfo.getQueryHistory().size();i++){
            if(queryInfo.getAppUserId().equals(ResultCodeEnum.SYSTEMVALUE.getCode()+"")){
                //TODO 没有userid 判断手机型号和地理位置是否有记录
                if(        qrInfo.getQueryHistory().get(i).getUserAgent().equals(ua)
                        && qrInfo.getQueryHistory().get(i).getAddressInfo().getAddressDetail().equals(queryInfo.getAddressInfo().getAddressDetail())
                        && qrInfo.getQueryHistory().get(i).getAddressInfo().getIpAddress().equals(queryInfo.getAddressInfo().getIpAddress())){
                    //TODO 存在相同记录次数不增加
                    timesFlag = true ;
                    break;
                }
                }else{
                    if(qrInfo.getQueryHistory().get(i).getAppUserId().equals(queryInfo.getAppUserId())){
                        //TODO 有userid 判断是否存在相同用户
                        timesFlag = true ;
                        break;
                    }

                }
        }
        if(timesFlag == false){
            times = qrInfo.getQueryHistory().size()+1;
            qrInfo.getQueryHistory().add(queryInfo);
            redisTemplate.opsForValue().set(qR, qrInfo);
        }
        return new TrackTimesResult(times, timesFlag);
    }

    @Override
    public QRInfo sendTrackMessage(String qR, QueryInfo queryInfo) throws Throwable {
        String correlationId = UUID.randomUUID().toString();
        Date sendTime = new Date();
        AMQP.BasicProperties props =
                new AMQP.BasicProperties("text/plain",
                        "UTF-8",
                        null,
                        2,
                        0, correlationId, "trackReplyMessageExchange", null,
                        null, sendTime, null, null,
                        "TrackProducer", null);


        MessageProperties sendMessageProperties =
                messagePropertiesConverter.toMessageProperties(props, null,"UTF-8");
        sendMessageProperties.setReceivedExchange("trackReplyMessageExchange");
        sendMessageProperties.setReceivedRoutingKey("trackReplyMessage");
        sendMessageProperties.setRedelivered(true);

        TrackMessage trackMessage = new TrackMessage();
        trackMessage.setQR(qR);
        trackMessage.setQueryInfo(queryInfo);
        String message = JSON.toJSONString(trackMessage);


        org.springframework.amqp.core.Message sendMessage = MessageBuilder.withBody(message.getBytes("UTF-8"))
                .andProperties(sendMessageProperties)
                .build();
        org.springframework.amqp.core.Message replyMessage = rabbitTemplate.sendAndReceive("trackMessageExchange",
                "trackMessage", sendMessage);

        String replyMessageContent = null;
        if (null == replyMessage) {
            throw new TimeoutException("mq reply timeout");
        }
        replyMessageContent = new String(replyMessage.getBody(),"UTF-8");
        log.info("Receive the apply from tracker {}", replyMessageContent);
        TrackApplyMessage trackApplyMessage = JSON.parseObject(replyMessageContent, TrackApplyMessage.class);
        if (trackApplyMessage.getExceptionCode() != 0) {
            switch (trackApplyMessage.getExceptionCode()) {
                case 1:
                    throw new FileNotFoundException();
                case 2:
                    throw new ProposalException(trackApplyMessage.getExceptionMessage());
                case 3:
                    throw new TimeoutException(trackApplyMessage.getExceptionMessage());
                case 4:
                    throw new FabricException(trackApplyMessage.getExceptionMessage());
                case 5:
                    throw new Exception("查询失败：此二维码正在被查询");
                case 6:
                    throw new Throwable(trackApplyMessage.getExceptionMessage());
                default:
                    break;
            }
        }

        return trackApplyMessage.getQrInfo();
    }

    class TrackTimesResult {

        private int times;

        private boolean flag;

        public int getTimes() {
            return times;
        }

        public void setTimes(int times) {
            this.times = times;
        }

        public boolean isFlag() {
            return flag;
        }

        public void setFlag(boolean flag) {
            this.flag = flag;
        }

        public TrackTimesResult(int times, boolean flag) {
            this.times = times;
            this.flag = flag;
        }
    }

    @Override
    public Message trackDetached(String qR, QueryInfo queryInfo, HttpServletRequest request) {
        return null;
    }
}
